<!---
	Copyright 2010 Edward Trudeau, Jeremy Battle
    This software is freely available under the MIT License: http://www.opensource.org/licenses/mit-license.php
    http://ayc.googlecode.com
    
--->
<cfcomponent>

	<cfset variables.instance = StructNew() />
    <cfset variables.instance.errors = StructNew() />
	<cffunction name="init" access="public" output="false">
		<cfargument name="arrFieldBeans" type="array" required="true" hint="array of field beans" />
		<cfargument name="ValidationService" type="any" required="true" hint="we need to have the validation service passed in" />
        
		<cfset setFieldBeans(arguments.arrFieldBeans) />	
		<cfset setVS(arguments.ValidationService) />
        <cfset setValidations() />
		<cfreturn this />
	</cffunction>
	
    
    <!--- Error Getters/Setters --->
	<cffunction name="clearStErrors" access="private" output="false" returntype="void">
		<cfset variables.instance.errors = structNew() />
	</cffunction>
		
	<cffunction name="getErrors" access="public" output="false" returntype="struct">
		<cfreturn variables.instance.errors />
	</cffunction>
    
    <cffunction name="updateErrors" access="private" output="false">
		<cfargument name="fieldName" type="string" required="true" />
		<cfargument name="errMsg" type="string" required="true" />
		
		<cfset structInsert(getErrors(),arguments.fieldName,arguments.errMsg,true) />
	</cffunction>
    
    <cffunction name="getFieldValue" access="public" returntype="any">
		<cfargument name="fieldName" type="string" required="true" />
		
		<cfif structKeyExists(form,arguments.fieldName)>
			<cfreturn form[arguments.fieldName] />
		</cfif>
		
		<cfreturn "" />
	</cffunction>
       
    <!--- FieldBeans Getters/Setter --->
	<cffunction name="getFieldBeans" access="public" returntype="array" output="false">
		<cfreturn variables.instance.fieldBeans />
	</cffunction>
    
    <cffunction name="setFieldBeans" access="public" returntype="void" output="false">
		<cfargument name="fieldBeans" type="array" required="true" />
		<cfset variables.instance.fieldBeans = arguments.fieldBeans />		
	</cffunction>
    
    <cffunction name="getVS" access="public" returntype="any" output="false">
		<cfreturn variables.instance.validationService />
	</cffunction>
    
    <cffunction name="setVS" access="public" returntype="void" output="false">
		<cfargument name="validationService" type="any" required="true" />
		<cfset variables.instance.validationService = arguments.validationService />		
	</cffunction>
    
    <cffunction name="setValidations" access="public" returntype="void" output="false">
    	<cfset var local = {} />
        <cfset local.stValidations = {} />
        <cfloop from="1" to="#arrayLen(getFieldBeans())#" index="local.i">
        	<cfset local.currentFieldId = getFieldBeans()[local.i].getFieldId() />
        	<cfset local.fieldValidations = getVS().getValidations(FieldId=local.currentFieldId) />
            <cfif arrayLen(local.fieldValidations)>
				<cfset local.stValidations[local.currentFieldId] = arrayNew(1) />
                <cfloop from="1" to="#arrayLen(local.fieldValidations)#" index="local.x">
                	<cfset local.stValidations[local.currentFieldId][local.x] = {} />
                    <cfset local.stValidations[local.currentFieldId][local.x].ValidationRule = local.fieldValidations[local.x].getValidationRule() />
                    <cfset local.stValidations[local.currentFieldId][local.x].ValidationMsg = local.fieldValidations[local.x].getValidationMsg() />
                    <cfset local.stValidations[local.currentFieldId][local.x].ValidationVal = local.fieldValidations[local.x].getValidationVal() />
                </cfloop>
            </cfif> 
        </cfloop>
        
        <cfset variables.instance.validations = local.stValidations />
    </cffunction>
    
    <cffunction name="getValidations" access="public" returntype="struct" output="false">
    	<cfreturn variables.instance.validations />
    </cffunction>
    
    <!--- Actual Validation Checks/Code Generation --->
    
  	<!--- Creates JS String to be used with jQuery.Validate plugin --->
	<cffunction name="getJSValidation" access="public" output="false" hint="Creates JS String to be used with jQuery.Validate plugin">
		<cfargument name="FormId" required="true" type="string" hint="id of the form" />
		<cfset var returnValue = "" />
		<cfset var i = 0 />
		<cfset var l = 0 />
		<cfset var v = 0 />
		<cfset var x = 0 />
		<cfset var y = 0 />
        	
            
        	<cfsavecontent variable="returnValue">
			$().ready(function(){
				<cfoutput>
				$("###arguments.FormId#").validate({
				
					rules:{
						<cfloop from="1" to="#arrayLen(getFieldBeans())#" index="i">
                        	<cfif structKeyExists(getValidations(),getFieldBeans()[i].getFieldId())>
                            	#getFieldBeans()[i].getFieldName()#: {
                                <cfloop from="1" to="#arrayLen(getValidations()[getFieldBeans()[i].getFieldId()])#" index="v">
                                	<cfif listFindNoCase("required-dependency",getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule)>
                                	required: "#getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal#"
                                    <cfelse>
                                    	<cfif len(trim(getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal)) and listLen(getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule) EQ 1>
                                       		#getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule#:
											<!--- Not sure about this chunk, this might need to be tweaked --->
											<cfif getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule EQ "equalTo">
                                            	'###getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal#'
                                            <cfelse>
                                            	#getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal#
                                            </cfif>
                                        <cfelseif len(trim(getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal)) and listLen(getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal) GT 1>
                                        #getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule#: [#getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationVal#]
                                        <cfelse>
                                        #getValidations()[getFieldBeans()[i].getFieldId()][v].ValidationRule#: true
                                        </cfif>
                                    </cfif>
                                    <cfif v LT arrayLen(getValidations()[getFieldBeans()[i].getFieldId()])>,</cfif>
                                </cfloop>
                                } 
								<cfif i LTE structCount( getValidations() )>,</cfif>
                            </cfif>                                                   
                        </cfloop>                          
					}
					messages:{
                    	<cfloop from="1" to="#arrayLen(getFieldBeans())#" index="x">
                        	<cfif structKeyExists(getValidations(),getFieldBeans()[x].getFieldId())>
                            	#getFieldBeans()[x].getFieldName()#: 
                                <cfif arrayLen(getValidations()[getFieldBeans()[x].getFieldId()]) EQ 1>
                                	<cfif len(trim(getValidations()[getFieldBeans()[x].getFieldId()][1].ValidationMsg))>
                                    	"#getValidations()[getFieldBeans()[x].getFieldId()][1].ValidationMsg#"
                                    <cfelse>
                                    	"#getjsValidationMsg(getFieldBeans()[x].getLabelText(),getValidations()[getFieldBeans()[x].getFieldId()][1].ValidationRule)#"
                                    </cfif>
                                    <cfif x LTE structCount( getValidations() )>,</cfif>
                                <cfelse>
                                {
                                <cfloop from="1" to="#arrayLen(getValidations()[getFieldBeans()[x].getFieldId()])#" index="y">
                                	#getValidations()[getFieldBeans()[x].getFieldId()][y].ValidationRule#:
                                    <cfif len(trim(getValidations()[getFieldBeans()[x].getFieldId()][y].ValidationMsg))>
                                    	"#getValidations()[getFieldBeans()[x].getFieldId()][y].ValidationMsg#"
                                    <cfelse>
                                    	"#getjsValidationMsg(getFieldBeans()[x].getLabelText(),getValidations()[getFieldBeans()[x].getFieldId()][y].ValidationRule)#"
                                    </cfif><cfif y LT arrayLen(getValidations()[getFieldBeans()[x].getFieldId()])>,</cfif>
                                </cfloop>
                                }<cfif x LTE structCount( getValidations() )>,</cfif>
                                </cfif>
                            </cfif>
                        </cfloop>
				});
				</cfoutput>
			});			
			</cfsavecontent>
			<cfreturn returnValue>
			<cfabort>
	
	</cffunction>
	
    <!--- Will validate form values on the server side --->
    <cffunction name="validateForm" access="public" hint="Will validate form values on the server side">
		<cfargument name="submittedForm" type="struct" required="true" hint="the form with the values to validate" />
        
        <cfset local = {} />
		<cfset local.form = arguments.submittedForm />
        
		<cfloop from="1" to="#arrayLen(getFieldBeans())#" index="local.i">
        	<cfparam name="local.form[getFieldBeans()[local.i].getFieldName()]" default="" />
			<cfset local.errors[getFieldBeans()[local.i].getFieldName()] = "" />
			<cfif structKeyExists(getValidations(),getFieldBeans()[local.i].getFieldId())>
				<cfloop from="1" to="#arrayLen(getValidations()[getFieldBeans()[local.i].getFieldId()])#" index="local.x">
					<cfif NOT testValidation(getFieldBeans()[local.i],getValidations()[getFieldBeans()[local.i].getFieldId()][local.x],local.form[getFieldBeans()[local.i].getFieldName()])>
						<cfif len(trim(getValidations()[getFieldBeans()[local.i].getFieldId()][local.x].ValidationMsg))>
							<cfset local.errors[getFieldBeans()[local.i].getFieldName()] = local.errors[getFieldBeans()[local.i].getFieldName()] & "<li>" & getValidations()[getFieldBeans()[local.i].getFieldId()][local.x].ValidationMsg & "</li>" />
						<cfelse>
							<cfset local.errors[getFieldBeans()[local.i].getFieldName()] = local.errors[getFieldBeans()[local.i].getFieldName()] & "<li>" & getjsValidationMsg(getFieldBeans()[local.i].getLabelText(),getValidations()[getFieldBeans()[local.i].getFieldId()][local.x].ValidationRule) & "</li>" />
						</cfif>
					</cfif>
					<cfset updateErrors(getFieldBeans()[local.i].getFieldName(),local.errors[getFieldBeans()[local.i].getFieldName()]) />
				</cfloop>
			</cfif>
		</cfloop>
		<cfreturn getErrors() />
    </cffunction>
    
    <!--- Does the actual validation test for each of the possible validation types, called by "validateForm()" --->
    <cffunction name="testValidation" access="private" returntype="boolean" hint="Does the actual validation test for each of the possible validation types, called by ""validateForm()""">
		<cfargument name="fieldBean" type="any" required="true" />
		<cfargument name="stValidation" type="struct" required="true" />
        <cfargument name="formValue" type="any" required="true" />
		
       
		<cfswitch expression="#arguments.stValidation.ValidationRule#">
			<cfcase value="required">
				<cfif NOT len(trim(arguments.formValue))>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>
			<cfcase value="email">
				<cfif len(trim(arguments.formValue)) AND NOT isValid("email",arguments.formValue)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="digits">
				<cfif len(trim(arguments.formValue)) AND NOT reFindNoCase("^[0-9]+$",arguments.formValue)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>
			<cfcase value="date">
				<cfif len(trim(arguments.formValue)) AND NOT isValid("date",arguments.formValue)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="url">
				<cfif len(trim(arguments.formValue)) AND NOT isValid("url",arguments.formValue)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="min">
				<cfif len(trim(arguments.formValue)) AND arguments.formValue LT arguments.stValidation.ValidationVal>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="max">
				<cfif len(trim(arguments.formValue)) AND arguments.formValue GT arguments.stValidation.ValidationVal>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="range">
				<cfif len(trim(arguments.formValue)) AND (arguments.formValue LT listFirst(arguments.stValidation.ValidationVal) OR arguments.formValue GT listLast(arguments.stValidation.ValidationVal))>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="number">
				<cfif len(trim(arguments.formValue)) AND NOT isValid("float",arguments.formValue)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="minlength">
				<cfif len(trim(arguments.formValue)) AND len(trim(arguments.formValue)) LT arguments.stValidation.ValidationVal>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfcase value="maxlength">
				<cfif len(trim(arguments.formValue)) AND len(trim(arguments.formValue)) GT arguments.stValidation.ValidationVal>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>
			<cfcase value="rangelength">
				<cfif len(trim(arguments.formValue)) AND (len(trim(arguments.formValue)) LT listFirst(arguments.stValidation.ValidationVal) OR len(trim(arguments.formValue)) GT listLast(arguments.stValidation.ValidationVal))>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>		
			<cfcase value="equalTo">
				<cfif len(trim(arguments.formValue)) AND arguments.formValue NEQ getFieldValue(arguments.stValidation.ValidationVal)>
					<cfreturn false>
				</cfif>
				<cfreturn true>
			</cfcase>	
			<cfdefaultcase>
				<cfreturn true>
			</cfdefaultcase>
		</cfswitch>
	</cffunction>
    
    <!--- If you just want CF server side validation code generated to be copy and pasted into a file use this function, however,
	its recommended to just use the internal validation function "validateForm()" --->
	<cffunction name="getServerSideValidation" access="public" output="false" hint="If you just want CF server side validation code generated to be copy and pasted into a file use this function, however, its recommended to just use the internal validation function ""validateForm()""">
		<cfset var returnValue = "" />
		<cfset var i = 0 />
		<cfset var l = 0 />
		<cfset var v = 0 />
		<cfset var x = 0 />
		<cfset var y = 0 />
		<cfif structKeyExists(variables.instance,"formFields")>
			<cfsavecontent variable="returnValue">
			<cfoutput>
			<cfloop from="1" to="#arrayLen(getFormFields())#" index="i">
				<cfif arrayLen(getFormFields()[i].validations)>
					<cfloop from="1" to="#arrayLen(getFormFields()[i].validations)#" index="x">
					#getcfValidationCode(getFormFields()[i],getFormFields()[i].validations[x])#				
					</cfloop>
				</cfif>
			</cfloop>
			</cfoutput>
			</cfsavecontent>
			<cfreturn returnValue />
		<cfelse>
			<cfreturn "Please initialize the validations using validationUtils.init(formFieldLocation)">
		</cfif>
	</cffunction>	
    
    
    <!--- Default messages if no validation message was provided --->
	<cffunction name="getValidationMsg" access="public" returntype="string" hint="Default messages if no validation message was provided">
		<cfargument name="labelText" type="string" required="true" />
		<cfargument name="stValidation" type="struct" required="true" />
		<cfswitch expression="#arguments.stValidation.rule#">
			<cfcase value="required">
				<cfreturn "Please supply a value for #arguments.labelText#." />
			</cfcase>
			<cfcase value="email">
				<cfreturn "Please supply a valid e-mail address for #arguments.labelText#." />
			</cfcase>
			<cfcase value="digits">
				<cfreturn "Please only supply a value containing numbers 0-9 for  #arguments.labelText#." />
			</cfcase>
			<cfcase value="date">
				<cfreturn "Please supply a valid date for #arguments.labelText#." />
			</cfcase>
			<cfcase value="url">
				<cfreturn "Please supply a valid url for #arguments.labelText#." />
			</cfcase>
			<cfcase value="min">
				<cfreturn "Please supply a numeric value with a minimum value of #arguments.stValidation.ValidationVal# for #arguments.labelText#." />
			</cfcase>
			<cfcase value="max">
				<cfreturn "Please supply a numeric value with a maximum value of #arguments.stValidation.ValidationVal# for #arguments.labelText#." />
			</cfcase>
			<cfcase value="range">
				<cfreturn "Please supply a numeric value with a value between #listFirst(arguments.stValidation.ValidationVal)# and #listLast(arguments.stValidation.ValidationVal)# for #arguments.labelText#." />
			</cfcase>
			<cfcase value="number">
				<cfreturn "Please supply a valid number for #arguments.labelText#." />
			</cfcase>
			<cfcase value="minlength">
				<cfreturn "Please supply a value no less than #arguments.stValidation.ValidationVal# characters long for  #arguments.labelText#." />
			</cfcase>
			<cfcase value="maxlength">
				<cfreturn "Please supply a value no more than #arguments.stValidation.ValidationVal# characters long for  #arguments.labelText#." />
			</cfcase>
			<cfcase value="rangelength">
				<cfreturn "Please supply a value between #listFirst(arguments.stValidation.ValidationVal)# and #listLast(arguments.stValidation.ValidationVal)# characters long for  #arguments.labelText#." />
			</cfcase>
			<cfcase value="equalTo">
				<cfreturn "The values for #arguments.labelText# and #arguments.stValidation.ValidationVal# must be equal." />
			</cfcase>
			<cfcase value="required-dependency">
				<cfreturn "Based on other field data #arguments.labelText# is required." />
			</cfcase>
		</cfswitch>
	</cffunction>   
	
    <!--- Utility Function(s) --->       
	<cffunction name="arrayOfStructsFind" access="private" returntype="numeric" output="false">
		<cfargument name="arrayToSearch" type="array" required="true" />
		<cfargument name="keyToSearch" type="string" required="true" />
		<cfargument name="valueToFind" type="string" required="true" />
		
		<cfset var result = 0 />
		<cfset var i = 0 />
		<cfset var key = "" />
		
		<cfloop from="1" to="#arrayLen(arguments.arrayToSearch)#" index="i">
			<cfif compareNoCase(arguments.arrayToSearch[i][arguments.keyToSearch],arguments.valueToFind) EQ 0>
				<cfreturn i>
			</cfif>
		</cfloop>
		
		<cfreturn 0 />
	</cffunction>
</cfcomponent>